package com.zcloud.eventstudydemo.llm;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文本分析器 - Java版本
 * 使用本地 Ollama 模型进行自然语言文本的意图解析
 */
public class AnalyzerLLMResult {
    private String ollamaUrl;
    private String modelName;
    private String apiUrl;
    private HttpClient httpClient;
    private ObjectMapper objectMapper;

    /**
     * 默认构造函数
     */
    public AnalyzerLLMResult() {
        this("http://localhost:11434", "qwen3:1.7b");
    }

    /**
     * 带参数的构造函数
     *
     * @param ollamaUrl Ollama 服务地址
     * @param modelName 使用的模型名称
     */
    public AnalyzerLLMResult(String ollamaUrl, String modelName) {
        this.ollamaUrl = ollamaUrl;
        this.modelName = modelName;
        this.apiUrl = ollamaUrl + "/api/generate";
        this.httpClient = HttpClient.newHttpClient();
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 调用 Ollama API 进行对话
     *
     * @param prompt       用户输入的问题
     * @param systemPrompt 系统提示词
     * @param maxRetries   最大重试次数
     * @return 模型返回的文本
     */
    public String callOllama(String prompt, String systemPrompt, int maxRetries) {
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                // 构造请求数据
                Map<String, Object> data = new HashMap<>();
                data.put("model", modelName);
                data.put("prompt", prompt);
                data.put("stream", false);

                Map<String, Object> options = new HashMap<>();
                options.put("temperature", 0.1);
                options.put("top_p", 0.9);
                options.put("max_tokens", 1000);
                data.put("options", options);

                // 如果有系统提示词，添加到请求中
                if (systemPrompt != null && !systemPrompt.trim().isEmpty()) {
                    data.put("system", systemPrompt);
                }

                // 发送请求
                String jsonBody = objectMapper.writeValueAsString(data);
                HttpRequest request = HttpRequest.newBuilder()
                        .uri(URI.create(apiUrl))
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                        .timeout(java.time.Duration.ofSeconds(30))
                        .build();

                HttpResponse<String> response = httpClient.send(request,
                        HttpResponse.BodyHandlers.ofString());

                if (response.statusCode() == 200) {
                    JsonNode result = objectMapper.readTree(response.body());
                    return result.has("response") ? result.get("response").asText() : "";
                } else {
                    System.err.println("Ollama API 调用失败，状态码: " + response.statusCode());
                    if (attempt < maxRetries) {
                        System.err.println("将在 2 秒后重试...");
                        Thread.sleep(2000);
                    } else {
                        return "";
                    }
                }
            } catch (Exception e) {
                System.err.println("请求 Ollama API 时发生错误 (尝试 " + (attempt + 1) + "/" + (maxRetries + 1) + "): " + e.getMessage());
                if (attempt < maxRetries) {
                    System.err.println("将在 2 秒后重试...");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return "";
                    }
                } else {
                    return "";
                }
            }
        }
        return "";
    }

    /**
     * 分析输入文本的意图
     *
     * @param text 输入文本
     * @return 包含意图、实体、行动、时间、地点的实体对象
     */
    public AnalysisResult analyzeIntent(String text) {
        if (text == null || text.trim().isEmpty()) {
            return emptyResult();
        }

        // 获取当前时间
        String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));

        // 系统提示词
        String systemPrompt = String.format("""
                        你是一个专业的业务意图分析助手。请分析用户输入的文本，提取以下信息：
                        1. related_entity：相关实体，指输入问题的主体对象（如：XX巡洋舰、项目名称、产品型号等），支持多个实体，用逗号分隔
                        2. time_start：事件开始时间，格式为YYYY-MM-DD HH:MM，仅当输入文本中明确询问或提及具体时间时才填写，如果用户提到相对时间（如明天、下周等），请基于当前时间%s计算具体时间
                        3. time_end：事件结束时间，格式为YYYY-MM-DD HH:MM，仅当输入文本中明确询问或提及具体时间时才填写，如果用户提到相对时间，请基于当前时间%s计算具体时间
                        4. event_content：事件详情，必须填写，包含完整的意图、行动、地点等信息，描述用户想要做什么或查询什么
                                      
                        重要说明：
                        - event_content字段必须填写，用于描述用户的完整意图和行动
                        - time_start和time_end字段仅在输入文本中明确询问或提及具体的时间安排、预约、会议、查询记录等时间相关活动时才填写
                        - 如果输入文本只是询问某个事物的时间信息（如"XX巡洋舰的下水时间"、"iPhone的发布时间"等），即使包含时间词汇，但目的是查询示例时间信息而非安排活动，则time_start和time_end应返回空字符串,除非明确是查询某一时间段的数据,如"今天的天气","2025年3月到5月之间发什么什么重大时间"等指定了查询时间范围的信息查询需要在time_start和time_end中填写正确的时间区间.
                        - 只有当输入文本包含具体的时间安排、预约、会议、活动等需要执行的时间相关任务时，才填写时间字段,或指定时间区间查询时也要填写时间字段
                        - 区分"查询时间信息"和"安排活动"：
                        * 查询时间信息：询问某个事物的属性、状态、历史等的时间信息（如"XX的下水时间"、"产品的发布时间"、"查询天气"等）→ 不需要时间字段\\
                        * 查询某一时间段的信息: 询问某个时间段的信息（如"2023年1月的天气"、"2023年1月1日到1月3日的天气"、"2023年1月1日到1月3日的新闻"等）→ 需要时间字段
                        * 安排活动：计划在特定时间做某事（如"明天开会"、"下午3点见面"、"预约医生"）→ 需要时间字段
                                    
                          
                        时间计算规则（仅当需要时使用）：
                        - 今天：%s 00:00 到 %s 23:59
                        - 明天：%s 00:00 到 %s 23:59
                        - 后天：%s 00:00 到 %s 23:59
                        - 如果用户只提到日期没有具体时间，默认使用00:00
                        - 如果用户只提到时间没有日期，使用今天或明天（根据上下文判断）
                                      
                        请以JSON格式返回结果，格式如下：
                        {
                            "related_entity": "相关实体1,相关实体2",
                            "time_start": "2023-10-05 00:00",
                            "time_end": "2023-10-05 23:59",
                            "event_content": "事件详情描述"
                        }
                                         
                        注意：
                        - event_content字段必须填写，不能为空，必须描述用户的完整意图和行动
                        - 如果time_start和time_end信息不存在，请返回空字符串
                        - 请确保返回的是有效的JSON格式
                        - 如果无法确定具体意图，event_content至少应该包含"用户想要"或"用户询问"等描述。
                        """,
                currentTime, currentTime,
                currentTime.split(" ")[0], currentTime.split(" ")[0],
                getTomorrowDate(), getTomorrowDate(),
                getDayAfterTomorrowDate(), getDayAfterTomorrowDate());

        // 用户提示词
        String userPrompt = "请分析以下文本的意图：\n\n" + text;

        // 调用模型
        String response = callOllama(userPrompt, systemPrompt, 3);

        if (response == null || response.trim().isEmpty()) {
            return emptyResult();
        }

        // 解析模型返回的JSON
        try {
            // 尝试提取JSON部分
            Pattern jsonPattern = Pattern.compile("\\{.*\\}", Pattern.DOTALL);
            Matcher jsonMatcher = jsonPattern.matcher(response);
            if (jsonMatcher.find()) {
                String jsonStr = jsonMatcher.group();
                JsonNode result = objectMapper.readTree(jsonStr);

                // 确保所有字段都存在，并标准化时间格式
                String eventContent = result.has("event_content") ?
                        result.get("event_content").asText().trim() : "";
                // 如果 event_content 为空，使用原始文本作为默认值
                if (eventContent.isEmpty()) {
                    eventContent = "用户输入: " + text;
                }

                AnalysisResult analysisResult = new AnalysisResult(
                        result.has("related_entity") ? result.get("related_entity").asText() : "",
                        normalizeTime(result.has("time_start") ? result.get("time_start").asText() : ""),
                        normalizeTime(result.has("time_end") ? result.get("time_end").asText() : ""),
                        eventContent
                );
                return ensureEventContent(analysisResult, text);
            } else {
                // 如果没有找到JSON，尝试手动解析
                AnalysisResult result = manualParse(response, text);
                return ensureEventContent(result, text);
            }
        } catch (Exception e) {
            System.err.println("解析JSON失败: " + e.getMessage());
            System.err.println("原始响应: " + response);
            AnalysisResult result = manualParse(response, text);
            return ensureEventContent(result, text);
        }
    }

    /**
     * 手动解析模型响应（当JSON解析失败时使用）
     *
     * @param response 模型响应文本
     * @param text     原始输入文本
     * @return 解析结果
     */
    private AnalysisResult manualParse(String response, String text) {
        String relatedEntity = "";
        String timeStart = "";
        String timeEnd = "";
        String eventContent = "";

        // 尝试从文本中提取信息
        String[] lines = response.split("\n");
        for (String line : lines) {
            line = line.trim();
            if (line.contains("related_entity") || line.contains("相关实体") || line.contains("实体")) {
                relatedEntity = line;
            } else if (line.contains("time_start") || line.contains("开始时间") || line.contains("开始")) {
                timeStart = line;
            } else if (line.contains("time_end") || line.contains("结束时间") || line.contains("结束")) {
                timeEnd = line;
            } else if (line.contains("event_content") || line.contains("事件详情") || line.contains("内容")) {
                eventContent = line;
            }
        }

        // 标准化时间格式
        timeStart = normalizeTime(timeStart);
        timeEnd = normalizeTime(timeEnd);

        // 确保 event_content 不为空
        if (eventContent.trim().isEmpty()) {
            eventContent = "用户输入: " + text;
        }

        return new AnalysisResult(relatedEntity, timeStart, timeEnd, eventContent);
    }

    /**
     * 测试 Ollama 连接
     *
     * @return 连接是否成功
     */
    public boolean testConnection() {
        try {
            // 尝试获取模型列表
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(ollamaUrl + "/api/tags"))
                    .timeout(java.time.Duration.ofSeconds(10))
                    .build();

            HttpResponse<String> response = httpClient.send(request,
                    HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                JsonNode result = objectMapper.readTree(response.body());
                JsonNode models = result.get("models");
                if (models != null && models.isArray()) {
                    for (JsonNode model : models) {
                        if (model.has("name") && model.get("name").asText().equals(modelName)) {
                            System.out.println("[成功] 找到模型: " + modelName);
                            return true;
                        }
                    }
                    System.err.println("[错误] 未找到模型: " + modelName);
                    return false;
                } else {
                    System.err.println("[错误] 无法获取模型列表");
                    return false;
                }
            } else {
                System.err.println("[错误] 无法获取模型列表，状态码: " + response.statusCode());
                return false;
            }
        } catch (Exception e) {
            System.err.println("[错误] 连接测试失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 获取明天的日期
     *
     * @return 明天的日期字符串
     */
    private String getTomorrowDate() {
        return LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 获取后天的日期
     *
     * @return 后天的日期字符串
     */
    private String getDayAfterTomorrowDate() {
        return LocalDateTime.now().plusDays(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 将时间字符串标准化为 YYYY-MM-DD HH:MM 格式
     *
     * @param timeStr 原始时间字符串
     * @return 标准化后的时间字符串
     */
    private String normalizeTime(String timeStr) {
        if (timeStr == null || timeStr.trim().isEmpty()) {
            return "";
        }

        // 如果已经是标准格式，直接返回
        Pattern standardPattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}");
        if (standardPattern.matcher(timeStr).matches()) {
            return timeStr;
        }

        // 获取当前时间
        LocalDateTime currentTime = LocalDateTime.now();
        String currentDate = currentTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 处理相对时间
        if (timeStr.contains("今天") || timeStr.contains("今日")) {
            if (timeStr.contains(":")) {
                // 提取时间部分
                Pattern timePattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
                Matcher timeMatcher = timePattern.matcher(timeStr);
                if (timeMatcher.find()) {
                    String hour = String.format("%02d", Integer.parseInt(timeMatcher.group(1)));
                    String minute = timeMatcher.group(2);
                    return currentDate + " " + hour + ":" + minute;
                }
            }
            return currentDate + " 00:00";
        } else if (timeStr.contains("明天") || timeStr.contains("明日")) {
            String tomorrow = getTomorrowDate();
            if (timeStr.contains(":")) {
                Pattern timePattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
                Matcher timeMatcher = timePattern.matcher(timeStr);
                if (timeMatcher.find()) {
                    String hour = String.format("%02d", Integer.parseInt(timeMatcher.group(1)));
                    String minute = timeMatcher.group(2);
                    return tomorrow + " " + hour + ":" + minute;
                }
            }
            return tomorrow + " 00:00";
        } else if (timeStr.contains("后天")) {
            String dayAfterTomorrow = getDayAfterTomorrowDate();
            if (timeStr.contains(":")) {
                Pattern timePattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
                Matcher timeMatcher = timePattern.matcher(timeStr);
                if (timeMatcher.find()) {
                    String hour = String.format("%02d", Integer.parseInt(timeMatcher.group(1)));
                    String minute = timeMatcher.group(2);
                    return dayAfterTomorrow + " " + hour + ":" + minute;
                }
            }
            return dayAfterTomorrow + " 00:00";
        }

        // 如果只包含时间，默认为今天
        Pattern timeOnlyPattern = Pattern.compile("\\d{1,2}:\\d{2}");
        if (timeOnlyPattern.matcher(timeStr).matches()) {
            Pattern timePattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
            Matcher timeMatcher = timePattern.matcher(timeStr);
            if (timeMatcher.find()) {
                String hour = String.format("%02d", Integer.parseInt(timeMatcher.group(1)));
                String minute = timeMatcher.group(2);
                return currentDate + " " + hour + ":" + minute;
            }
        }

        // 默认返回原字符串
        return timeStr;
    }

    /**
     * 返回空结果
     *
     * @return 空的分析结果
     */
    private AnalysisResult emptyResult() {
        return new AnalysisResult("", "", "", "无法分析输入文本");
    }

    /**
     * 确保 event_content 字段有值
     *
     * @param result       分析结果
     * @param originalText 原始文本
     * @return 确保有event_content的结果
     */
    private AnalysisResult ensureEventContent(AnalysisResult result, String originalText) {
        if (result.getEventContent() == null || result.getEventContent().trim().isEmpty()) {
            result.setEventContent("用户输入: " + originalText);
        }
        return result;
    }
}
